package com.nekocode.http;

import android.app.Activity;
import android.content.Context;
import android.os.Handler;
import android.support.v4.app.Fragment;
import android.text.TextUtils;

import com.nekocode.utils.FileManager;
import com.nekocode.utils.ImageUtility;
import com.nekocode.utils.MyAsyncTask;

import java.lang.ref.WeakReference;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * User: qii
 * Date: 13-2-9
 */
public class TaskCache {

    private static final ThreadFactory sDownloadThreadFactory = new ThreadFactory() {
        private final AtomicInteger mCount = new AtomicInteger(1);

        public Thread newThread(Runnable r) {
            return new Thread(r, "DownloadFutureTask Download #" + mCount.getAndIncrement());
        }
    };

    private static final Executor DOWNLOAD_THREAD_POOL_EXECUTOR
            = new ThreadPoolExecutor(4, 4, 1,
            TimeUnit.SECONDS, new LinkedBlockingDeque<Runnable>(15) {
        @Override
        public boolean offer(Runnable runnable) {
            return super.offerFirst(runnable);
        }

    }, sDownloadThreadFactory,
            new ThreadPoolExecutor.DiscardOldestPolicy() {
                @Override
                public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
                    if (!e.isShutdown()) {
                        LinkedBlockingDeque<Runnable> deque = (LinkedBlockingDeque) e.getQueue();
                        Runnable runnable = deque.pollLast();
                        if (runnable instanceof DownloadFutureTask) {
                            DownloadFutureTask futureTask = (DownloadFutureTask) runnable;
                            futureTask.cancel(true);
                            String url = futureTask.getUrl();
                            removeDownloadTask(url, futureTask);
                        }
                        e.execute(r);
                    }
                }
            }
    );

    private static ConcurrentHashMap<String, DownloadFutureTask> downloadTasks
            = new ConcurrentHashMap<String, DownloadFutureTask>();

    public static final Object backgroundWifiDownloadPicturesWorkLock = new Object();

    public static void removeDownloadTask(String url, DownloadFutureTask downloadWorker) {
        synchronized (TaskCache.backgroundWifiDownloadPicturesWorkLock) {
            downloadTasks.remove(url, downloadWorker);
            if (TaskCache.isDownloadTaskFinished()) {
                TaskCache.backgroundWifiDownloadPicturesWorkLock.notifyAll();
            }
        }

    }


    public static boolean isDownloadTaskFinished() {
        return TaskCache.downloadTasks.isEmpty();
    }

    public static boolean isThisUrlTaskFinished(String url) {
        return !downloadTasks.containsKey(url);
    }


    private static boolean waitForPictureDownload(Context context, String url,
            FileDownloaderHttpHelper.DownloadListener downloadListener, String savedPath) {
        while (true) {
            DownloadFutureTask downloadFutureTask = TaskCache.downloadTasks.get(url);

            if (downloadFutureTask == null) {

                DownloadFutureTask newDownloadFutureTask = DownloadFutureTask
                        .newInstance(context, url);
                synchronized (backgroundWifiDownloadPicturesWorkLock) {
                    downloadFutureTask = TaskCache.downloadTasks
                            .putIfAbsent(url, newDownloadFutureTask);
                }
                if (downloadFutureTask == null) {
                    downloadFutureTask = newDownloadFutureTask;
                    DOWNLOAD_THREAD_POOL_EXECUTOR.execute(downloadFutureTask);
                }

            }

            downloadFutureTask.addDownloadListener(downloadListener);

            try {
                return downloadFutureTask.get();
            } catch (InterruptedException e) {
                //for better listview scroll performance
                downloadFutureTask.cancel(true);
                Thread.currentThread().interrupt();
                return false;
            } catch (ExecutionException e) {
                return false;
            } catch (CancellationException e) {
                removeDownloadTask(url, downloadFutureTask);
            }

        }
    }

    public static class DownloadCallback {

        public void onSubmitJobButNotBegin() {

        }

        public void onSubmitJobButAlreadyBegin() {

        }

        public void onBegin() {

        }

        public void onUpdate(int value, int max) {

        }

        public void onComplete(String localPath) {

        }
    }

    public static void download(final Object object, final Handler handler, final String url,
                               final DownloadCallback callback) {

        if (TextUtils.isEmpty(url)) {
            return;
        }

        if (TaskCache.isThisUrlTaskFinished(url)) {
            callback.onSubmitJobButNotBegin();
        } else {
            callback.onSubmitJobButAlreadyBegin();
        }

        Context c = null;
        if (object instanceof Fragment) {
            c = ((Fragment) object).getActivity();
        } else if (object instanceof Activity) {
            c = (Activity) object;
        }
        final Context context = c;


        new MyAsyncTask<Void, Integer, String>() {

            WeakReference<Object> activityRef;

            @Override
            protected void onPreExecute() {
                super.onPreExecute();
                activityRef = new WeakReference<Object>(object);
                callback.onBegin();
            }

            @Override
            protected String doInBackground(Void... params) {

                String path = FileManager.getFilePathFromUrl(context, url);

                if (!(ImageUtility.isThisBitmapCanRead(path) && TaskCache
                        .isThisUrlTaskFinished(url))) {
                    boolean downloaded = TaskCache.waitForPictureDownload(context,
                            url, new FileDownloaderHttpHelper.DownloadListener() {
                                @Override
                                public void pushProgress(final int progress, final int max) {
                                    handler.post(new Runnable() {
                                        @Override
                                        public void run() {
                                            onProgressUpdate(progress, max);
                                        }
                                    });
                                }

                                @Override
                                public void completed() {

                                }

                                @Override
                                public void cancel() {

                                }
                            }, FileManager.getFilePathFromUrl(context, url));
                    if (downloaded) {
                        path = FileManager.getFilePathFromUrl(context, url);
                    }
                }

                return path;

            }

            @Override
            protected void onProgressUpdate(Integer... values) {
                super.onProgressUpdate(values);
                if (!isComponentLifeCycleFinished()) {
                    int progress = values[0];
                    int max = values[1];
                    callback.onUpdate(progress, max);
                }
            }

            @Override
            protected void onPostExecute(String value) {
                super.onPostExecute(value);
                if (!isComponentLifeCycleFinished()) {
                    callback.onComplete(value);
                }

            }

            boolean isComponentLifeCycleFinished() {
                Object object = activityRef.get();
                if (object == null) {
                    return true;
                }

                if (object instanceof Fragment) {
                    Fragment fragment = (Fragment) object;
                    if (fragment.getActivity() == null) {
                        return true;
                    }
                } else if (object instanceof Activity) {
                    Activity activity = (Activity) object;
                    if (activity.isDestroyed()) {
                        return true;
                    }
                }

                return false;
            }

        }.executeOnExecutor(MyAsyncTask.THREAD_POOL_EXECUTOR);
    }


}
